package pacBiopackage;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;

import tools.fasta.FastaSeq;

public class PacBioCorrectionByAlignmentVcf implements CorretionbyAlignment {

	FastaSeq fs = null;
	int frame_counter=0;
	private long prevBufferReaderposition = 0;
	PacBioReadVCF alignmentReader = null;
	private List<String> row = null;
	private List<Integer> corrected=null;
	private PacBioErrorStatisticsContainer stat = null;
	
	public PacBioCorrectionByAlignmentVcf(PacBioReadVCF pbrs,PacBioErrorStatisticsContainer stat)
	{
		this.alignmentReader = pbrs;
		this.stat=stat;
	}
	
	
	private StringBuffer fillUnknown()
	{
		StringBuffer tempSequence = new StringBuffer(fs.getSeq());
		corrected = new ArrayList<Integer>();
		return tempSequence;
	}
	
	private void updateStatistic(int mode)
	{
		StringBuilder sb = new StringBuilder();
		int tempBeginIndex = Integer.valueOf(row.get(2))-5;
		int tempEndIndex = Integer.valueOf(row.get(2))+5;
		if(tempBeginIndex<0)tempBeginIndex=0;
		if(tempEndIndex>this.fs.getSeq().length())tempEndIndex=this.fs.getSeq().length();
		sb.append(this.fs.getSeq().substring(tempBeginIndex, Integer.valueOf(row.get(2))));
		sb.append(this.fs.getSeq().substring(Integer.valueOf(row.get(2)),tempEndIndex));
		
		if(mode==0)
		{		
			updateMap(stat.getInsertEnviourment(),sb.toString());
		}
		else if(mode==1)
		{
			updateMap(stat.getDeletionEnviourment(), sb.toString());
		}
		else if(mode==2)
		{
			updateMap(stat.getSNPEnviourment(), sb.toString());
		}
	}
	
	private void updateMap(HashMap<String,Integer> map,String sb)
	{
		if(map.containsKey(sb))
		{
			map.put(sb, map.get(sb)+1);
		}
		else
		{
			map.put(sb,1);
		}
	}
	private Pair<StringBuffer,Integer>snp(StringBuffer tempSequence)
	{
		tempSequence.append(alignmentReader.getSNP(row).get(0));
		String ref = alignmentReader.getRef(row);
		String snp = alignmentReader.getSNP(row).get(0);
		updateStatistic(2);
		stat.getErrors().put("mis", stat.getErrors().get("mis")+1);
		stat.getMismatchErrorType().get(ref).put(alignmentReader.getSNP(row).get(0), stat.getMismatchErrorType().get(ref).get(snp)+1);
		stat.getPositionsErrors2().put(alignmentReader.getPosition(row),stat.getPositionsErrors2().get(alignmentReader.getPosition(row))+1);
			
		return new Pair<StringBuffer, Integer>(tempSequence,alignmentReader.getPosition(row)-1);
	}
	
	
	private Pair<StringBuffer,Integer>deletion(StringBuffer tempSequnce)
	{
		updateStatistic(1);
		int leng=Math.abs(alignmentReader.getDiff(row));
		int realDeletionPosition = alignmentReader.getPosition(row)+ alignmentReader.getRef(row).length()-1;
		for(int j=0; j < Integer.valueOf(leng);++j)
		{
			stat.getErrors().put("del",stat.getErrors().get("del")+1);
			--frame_counter;
			stat.getPositionsErrors2().put(realDeletionPosition-j,stat.getPositionsErrors2().get(realDeletionPosition-j)+1);
		}		
		tempSequnce.append(alignmentReader.getSNP(row).get(0));
		return new Pair<StringBuffer, Integer>(tempSequnce,realDeletionPosition);
	}
	
	private Pair<StringBuffer,Integer> insert(StringBuffer tempSequence)
	{
		int realInsertPosition = alignmentReader.getPosition(row)+ alignmentReader.getRef(row).length()-1;
		int leng=Math.abs(alignmentReader.getDiff(row));
		
		stat.getPositionsErrors2().put(realInsertPosition, stat.getPositionsErrors2().get(realInsertPosition)+leng);
		stat.getErrors().put("ins",stat.getErrors().get("ins")+leng);
		//first construct the string
		updateStatistic(0);
		//stat.getInsertEnviourment().
		String instertSNP = alignmentReader.getSNP(row).get(0);
		int reflength = alignmentReader.getRef(row).length();
		for(int j=0; j < leng;++j)
		{
			char toBeInserted = instertSNP.charAt(reflength+j);
			if(toBeInserted != 'N')
			{
				stat.getInsertFreq().put(String.valueOf(toBeInserted), stat.getInsertFreq().get(String.valueOf(toBeInserted))+1);
			}
			++frame_counter;
		}
		tempSequence.append(instertSNP);		
		return new Pair<StringBuffer, Integer>(tempSequence,realInsertPosition);		
	}
	
	
	
	
	private boolean setFilePointer(String id) throws IOException
	{
		//first check if we already have seen the id
		long filePosition  = 0;
		boolean flag=false;
		if(prevBufferReaderposition!=0)
		{
			filePosition = prevBufferReaderposition;
			alignmentReader.setFilePosition(prevBufferReaderposition);
		}
		else
		{
			filePosition=alignmentReader.getFilePosition();
		}			
		if(alignmentReader.ready())
		{
			List<String> tempRow = alignmentReader.next();
			if(tempRow.get(0).equals(id))
			{
				row = tempRow;
				flag=true;
			}
			else
			{
				//now we have to search until we find the correct file pointer position
				StringTokenizer st = new StringTokenizer(id,"/");
				String name = st.nextToken();
				int identifier = Integer.valueOf(st.nextToken()); //zwei geteilt
				while(alignmentReader.ready())
				{
					filePosition=alignmentReader.getFilePosition();
					tempRow = alignmentReader.next();
					if(tempRow.get(0).equals(id))
					{
						alignmentReader.setFilePosition(filePosition);
						row = tempRow;
						flag=true;
						break;
					}
					else
					{
						st = new StringTokenizer(tempRow.get(0),"/");
						String name2 = st.nextToken();
						int identifier2 = Integer.valueOf(st.nextToken()); //zwei geteilt
						if((name2.equals(name) && identifier2 >identifier)||name2.compareTo(name)<0)
						{
							flag=false;
							break;
						}
					}
				}
			}
		}
		if(flag)
		{
			prevBufferReaderposition = alignmentReader.getFilePosition();
		}
		return flag;
	}
	
	private StringBuffer correctIt(int counter)
	{
		//now we have to go until we find an entry which is not part of this read.
		
		StringBuffer tempSequence = new StringBuffer();
		String header = alignmentReader.getChr(row);
		corrected = new ArrayList<Integer>();
		int i=0;
		boolean out=false;
		try
		{
			while(!out && alignmentReader.ready())
			{			
				if(i+1<Integer.valueOf(alignmentReader.getPosition(row)) && i < fs.getSeq().length())
				{
					//notCorrected.add(tempSequence.length());
					stat.getPositionsErrors().put(tempSequence.length(), stat.getPositionsErrors().get(tempSequence.length())+1); //do i have to consider the frameshift? or just refere to the orignal string?... -> would perefere the orginal string.... than i have to change it! TODO
					tempSequence.append(fs.getSeq().charAt(i));
					++i;
					continue;
				}
				if(i+1 == alignmentReader.getPosition(row))
				{
					corrected.add(tempSequence.length());
					Pair<StringBuffer,Integer> temp=null;
					if(alignmentReader.getType(row)==2) //inserts
					{
						temp= insert(tempSequence);						
					}
					else if(alignmentReader.getType(row)==1) //deletion
					{
						temp=deletion(tempSequence);
					}
					else if(alignmentReader.getType(row)==0) //SNP
					{
						temp=snp(tempSequence);
					}
					tempSequence=temp.getFirst();
					i=temp.getSecond()-1;
					try
					{
						prevBufferReaderposition = alignmentReader.getFilePosition();
						if(alignmentReader.ready())
						{
							row = alignmentReader.next();
						}
						else
						{
							out=true;
						}
					}
					catch(Exception e)
					{
						e.printStackTrace();
					}
					try
					{
						if(!alignmentReader.getChr(row).equals(header) && !out)
						{
							out=true;
						}
					}
					catch(Exception e)
					{
						e.printStackTrace();
					}
				}
				++i;
			}		
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
		while(i<fs.getSeq().length())
		{
			//notCorrected.add(i+frame_counter);
			tempSequence.append(fs.getSeq().charAt(i));
			++i;
		}
		return tempSequence;
	}
	
	
	
	public StringBuffer correctByAlignment(FastaSeq fs,int counter)
	{
		//first find the correct identifier in the alignmentReader stream
		this.fs=fs;
		this.frame_counter=0;
		String header = null;
		//StringTokenizer st = new StringTokenizer(fs.getHeaderName()," ");
		StringTokenizer st = new StringTokenizer(fs.getQname()," ");
		header = st.nextToken();
		st=null;
		StringBuffer corrected_sequence = null;
		try
		{
			boolean found = setFilePointer(header);
			//now we have to correct the read
			if(found)
			{
				corrected_sequence = correctIt(counter);
			}
			else
			{
				corrected_sequence=fillUnknown();
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}		
		return corrected_sequence;
	}
	

	@Override
	public List<Integer> getPositions() {
		return this.corrected;
	}

}
